package ManageGameHServer.classFile;

import ManageGameHServer.conf.Setup;

import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 日志数据库连接
 * @author lijie1
 */

class LogQueueConOne{
    public  Connection con;               //数据库连接对象
    public  int upUseTime;               //上次使用的时间戳
    public LogQueueConOne(Connection con,int upUseTime){
        this.con=con;
        this.upUseTime=upUseTime;
    }
}

class LogWaitingGetConOne{
    public LinkedBlockingQueue<Connection> get;
    public int stateInt;
    public LogWaitingGetConOne(LinkedBlockingQueue<Connection> get,int stateInt){
        this.get=get;
        this.stateInt=stateInt;
    }
}


public class LogDBConnectO {
    public static int DBConnectOKeepTimeInt;
    public static final Integer MainLock=1;
    public static final Integer SaveLock=1;
    public static LinkedBlockingQueue<LogWaitingGetConOne> LogWaitingGetConBufferQueue=new LinkedBlockingQueue<>(20000);                 //等待获得连接等待池子
    public static LinkedBlockingQueue<LogWaitingGetConOne> LogWaitingGetSaveConBufferQueue=new LinkedBlockingQueue<>(20000);             //等待获得从连接等待池子
    public static int nowMainConSum=0;                                                                                                       //现在主要连接维持数
    public static int nowSaveConSum=0;                                                                                                       //限制从连接的维持数
    public static ArrayBlockingQueue<LogQueueConOne> LogChangeConnectionQueue =new ArrayBlockingQueue<>(2000);
    public static ArrayBlockingQueue<LogQueueConOne> LogReadConnectionQueue =new ArrayBlockingQueue<>(2000);
    public static void Initialization(){
        System.out.println("LogDBConnectO 日志数据库"+Setup.LogDBtable+"连接池开始启动......");
        try {
            Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
        } catch (Exception ex) {
            System.out.println(">>>>>>>>>>>>>"+ex.getMessage());
        }
        synchronized (MainLock) {
            LogWaitingGetConBufferQueue = new LinkedBlockingQueue<>(20000);                 //等待获得连接等待池子
            LogChangeConnectionQueue = new ArrayBlockingQueue<>(2000);
        }
        synchronized (SaveLock) {
            LogWaitingGetSaveConBufferQueue = new LinkedBlockingQueue<>(20000);             //等待获得从连接等待池子
            LogReadConnectionQueue = new ArrayBlockingQueue<>(2000);
        }
        int nowTime=(int)(System.currentTimeMillis()/1000);
        int row;
        try {
            String LogDBTable= Setup.LogDBtable;
            for (row=1; row <= Setup.LogDBPoolMinSum; row++) {
                //主
                Connection connection= DriverManager.getConnection("jdbc:mysql://"+ Setup.LogDBurl+":"+Setup.LogDBport+"/"+LogDBTable+"?characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8",Setup.LogDBuser,Setup.LogDBpassword);
                LogChangeConnectionQueue.add(new LogQueueConOne(connection,nowTime));

                nowMainConSum++;
                //副
                Connection readConnection= DriverManager.getConnection("jdbc:mysql://"+ Setup.LogDBurlS+":"+Setup.LogDBport+"/"+LogDBTable+"?characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8",Setup.LogDBuser,Setup.LogDBpassword);
                LogReadConnectionQueue.add(new LogQueueConOne(readConnection,nowTime));
                nowSaveConSum++;
            }
            //主连接逻辑分配
            new LogMainArrayDistributionWorker().start();
            //副连接逻辑分配
            new LogSaveArrayDistributionWorker().start();

            //启动数据库连接心跳线  1分钟运行一下
            new Timer().schedule(new LogKeepWorker(), 60*1000, 60*1000);

            //检测心跳线管理定时器
            new Timer().schedule(new LogKeepWorkerManange(), 180*1000, 180*1000);



        }catch (SQLException SqlE){
            System.out.println("地址"+Setup.LogDBurl+" 端口"+Setup.LogDBport+" 库名"+Setup.LogDBtable+ " 用户名"+Setup.LogDBuser+" 密码" +Setup.LogDBpassword);
            System.out.println("LogDBConnectO :: "+SqlE.getMessage());
            SqlE.printStackTrace();
            System.out.println(SqlE);
        }
    }
    //state  1 表示有写入权限 2表示只读取
    public static Connection getCon(int state) throws Exception{
         LinkedBlockingQueue<Connection> get=new LinkedBlockingQueue<>(1);
         try {
             if(state==1) {
                 LogDBConnectO.LogWaitingGetConBufferQueue.put(new LogWaitingGetConOne(get, state));
             }else {
                 LogDBConnectO.LogWaitingGetSaveConBufferQueue.put(new LogWaitingGetConOne(get, state));
             }
             return get.take();
         }catch (InterruptedException ie){
             Log.errorLog(ie.getMessage());
             Log.errorLog(ie.getStackTrace());
             throw new Exception(ie.getMessage());
         }
    }
    public static void redCon(int state,Connection connection){
        try {
            int nowTime=(int)(System.currentTimeMillis()/1000);
            if(state==1){
                LogChangeConnectionQueue.add(new LogQueueConOne(connection,nowTime));
                System.out.println("放回数："+LogChangeConnectionQueue.size());
            }else {
                LogReadConnectionQueue.add(new LogQueueConOne(connection,nowTime));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

class LogMainArrayDistributionWorker extends Thread{
    @Override
    public void run() {
        while (true){
            try{
                LogWaitingGetConOne waitingGetConOne = LogDBConnectO.LogWaitingGetConBufferQueue.take();
                Connection connection=null;
                System.out.println("等到数量："+LogDBConnectO.LogWaitingGetConBufferQueue.size());
                //System.out.println(LogDBConnectO.nowMainConSum<Setup.LogDBPoolMaxSum);

                if(LogDBConnectO.nowMainConSum<Setup.LogDBPoolMaxSum){
                    boolean is=true;
                    synchronized (LogDBConnectO.MainLock){
                        if(LogDBConnectO.LogChangeConnectionQueue.size()>0){
                            LogQueueConOne  queueConOne=LogDBConnectO.LogChangeConnectionQueue.take();
                            connection=queueConOne.con;
                            is=false;
                        }
                    }
                    if(is){
                        try {
                            connection= DriverManager.getConnection("jdbc:mysql://"+ Setup.LogDBurl+":"+Setup.LogDBport+"/"+Setup.LogDBtable+"?characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8",Setup.LogDBuser,Setup.LogDBpassword);
                        }catch (Exception  e){
                            e.printStackTrace();
                        }
                        LogDBConnectO.nowMainConSum++;
                    }
                }else {                               //已经最大位置
                    LogQueueConOne  queueConOne=LogDBConnectO.LogChangeConnectionQueue.take();
                    connection=queueConOne.con;

                }
                if(connection!=null) {
                    waitingGetConOne.get.put(connection);
                }else {
                    System.out.println("获得连接严重错误");
                    Log.errorLog("获得主连接严重错误");
                }
            }catch (Exception e){
                Log.errorLog(e.getMessage());
                Log.errorLog(e.getStackTrace());
            }
        }
    }
}

class  LogSaveArrayDistributionWorker extends Thread{
    @Override
    public void run() {
        while (true){
            try{
                LogWaitingGetConOne waitingGetConOne = LogDBConnectO.LogWaitingGetSaveConBufferQueue.take();
                Connection connection=null;
                if(LogDBConnectO.nowSaveConSum<Setup.LogDBPoolMaxSum){
                    boolean is=true;
                    synchronized (LogDBConnectO.SaveLock){
                        if(LogDBConnectO.LogReadConnectionQueue.size()>0){
                            LogQueueConOne  queueConOne=LogDBConnectO.LogReadConnectionQueue.take();
                            connection=queueConOne.con;
                            is=false;
                        }
                    }
                    if(is){
                        connection= DriverManager.getConnection("jdbc:mysql://"+ Setup.LogDBurlS+":"+Setup.LogDBport+"/"+Setup.LogDBtable+"?characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8",Setup.LogDBuser,Setup.LogDBpassword);
                        LogDBConnectO.nowMainConSum++;
                    }
                }else {                               //已经最大位置
                    LogQueueConOne  queueConOne=LogDBConnectO.LogReadConnectionQueue.take();
                    connection=queueConOne.con;

                }
                if(connection!=null) {
                    waitingGetConOne.get.put(connection);
                }else {
                    System.out.println("获得连接严重错误");
                    Log.errorLog("获得主连接严重错误");
                }
            }catch (Exception e){
                Log.errorLog(e.getMessage());
                Log.errorLog(e.getStackTrace());
            }
        }
    }
}


class LogKeepWorkerManange extends TimerTask{
    @Override
    public void run() {
        int nowTime=(int)(System.currentTimeMillis()/1000);
        if( nowTime -LogDBConnectO.DBConnectOKeepTimeInt>600){    //表示心跳线死亡需要重启心跳
            Log.errorLog("监听到数据库连接池心跳死亡，重启一下心跳 上次最后一下心跳"+LogDBConnectO.DBConnectOKeepTimeInt+" 本次启动时间:"+nowTime);
            new Timer().schedule(new KeepWorker(), 60, 60);
        }
    }
}

class  LogKeepWorker extends TimerTask {
    public int runSum=0;
    public LogKeepWorker(){
        LogDBConnectO.DBConnectOKeepTimeInt=(int)(System.currentTimeMillis()/1000);
    }
    public void run() {                         //一分钟运行一次
        //喂狗
        int nowTime=(int)(System.currentTimeMillis()/1000);
        LogDBConnectO.DBConnectOKeepTimeInt=nowTime;
        Log.errorLog("data heart .startTime："+System.currentTimeMillis());
        try {
            try {
                synchronized (LogDBConnectO.MainLock) {
                    if (LogDBConnectO.LogChangeConnectionQueue.size() > 0) {
                        int sum = LogDBConnectO.LogChangeConnectionQueue.size() - Setup.LogDBPoolMinSum;
                        ArrayList<LogQueueConOne> delArray = new ArrayList<>();
                        out:
                        for (LogQueueConOne queueConOne : LogDBConnectO.LogChangeConnectionQueue) {
                            if (sum > 0) {
                                if (nowTime - queueConOne.upUseTime >= 120) {      //等分钟关闭
                                    delArray.add(queueConOne);
                                    sum--;
                                }
                            } else {
                                break out;
                            }
                        }
                        if (delArray.size() > 0) {
                            for (LogQueueConOne queueConOne : delArray) {
                                LogDBConnectO.LogChangeConnectionQueue.remove(queueConOne);
                                if(queueConOne.con!=null) {
                                    queueConOne.con.close();
                                }
                                LogDBConnectO.nowMainConSum--;
                            }
                        }
                    }
                }
                synchronized (LogDBConnectO.SaveLock) {
                    if (LogDBConnectO.LogReadConnectionQueue.size() > 0) {
                        int sum = LogDBConnectO.LogReadConnectionQueue.size() - Setup.LogDBPoolMinSum;
                        ArrayList<LogQueueConOne> delArray = new ArrayList<>();
                        out:
                        for (LogQueueConOne queueConOne : LogDBConnectO.LogReadConnectionQueue) {
                            if (sum > 0) {
                                if (nowTime - queueConOne.upUseTime >= 120) {      //等分钟关闭
                                    delArray.add(queueConOne);
                                    sum--;
                                }
                            } else {
                                break out;
                            }
                        }
                        if (delArray.size() > 0) {
                            for (LogQueueConOne queueConOne : delArray) {
                                LogDBConnectO.LogReadConnectionQueue.remove(queueConOne);
                                if(queueConOne.con!=null) {
                                    queueConOne.con.close();
                                }
                                LogDBConnectO.nowSaveConSum--;
                            }
                        }
                    }
                }
            }catch (Exception e){
                Log.errorLog(e.getMessage());
                Log.errorLog(e.getStackTrace());
            }

            try {
                if (runSum >= 30) {
                    synchronized (LogDBConnectO.MainLock) {
                        try {
                            ArrayList<LogQueueConOne> delArray = new ArrayList<>();
                            for (LogQueueConOne queueConOne : LogDBConnectO.LogChangeConnectionQueue) {
                                try {
                                    Statement st = queueConOne.con.createStatement();
                                    ResultSet resultSet = st.executeQuery("show tables");
                                    resultSet.close();
                                    st.close();
                                } catch (Exception e) {
                                    delArray.add(queueConOne);
                                    Log.errorLog(e.getMessage());
                                    Log.errorLog(e.getStackTrace());
                                }
                            }
                            if (delArray.size() > 0) {
                                for (LogQueueConOne queueConOne : delArray) {
                                    LogDBConnectO.LogChangeConnectionQueue.remove(queueConOne);
                                    if (queueConOne.con != null) {
                                        queueConOne.con.close();
                                    }
                                    LogDBConnectO.nowMainConSum--;
                                }
                            }
                        }catch (Exception e){
                            Log.errorLog(e.getMessage());
                            Log.errorLog(e.getStackTrace());
                        }
                    }
                    synchronized (LogDBConnectO.SaveLock) {
                        try {
                            ArrayList<LogQueueConOne> delArray = new ArrayList<>();
                            for (LogQueueConOne queueConOne : LogDBConnectO.LogReadConnectionQueue) {
                                try {
                                    Statement st = queueConOne.con.createStatement();
                                    ResultSet resultSet = st.executeQuery("show tables");
                                    resultSet.close();
                                    st.close();
                                } catch (Exception e) {
                                    delArray.add(queueConOne);
                                    Log.errorLog(e.getMessage());
                                    Log.errorLog(e.getStackTrace());
                                }
                            }
                            if (delArray.size() > 0) {
                                for (LogQueueConOne queueConOne : delArray) {
                                    LogDBConnectO.LogReadConnectionQueue.remove(queueConOne);
                                    if (queueConOne.con != null) {
                                        queueConOne.con.close();
                                    }
                                    LogDBConnectO.nowSaveConSum--;
                                }
                            }
                        }catch (Exception e){
                            Log.errorLog(e.getMessage());
                            Log.errorLog(e.getStackTrace());
                        }
                    }
                    runSum = 0;
                } else {
                    runSum++;
                }
            }catch (Exception e){
                Log.errorLog(e.getMessage());
                Log.errorLog(e.getStackTrace());
            }
        }catch(Exception e) {
            Log.errorLog(e.getMessage());
            Log.errorLog(e.getStackTrace());
        }
        Log.errorLog("data heart. endTime："+System.currentTimeMillis()+"  changeSum："+LogDBConnectO.LogChangeConnectionQueue.size()+" readSum："+LogDBConnectO.LogReadConnectionQueue.size());
   }
}